// bts_DibExApi.cpp: implementation of the bts_DibExApi class.
//
//////////////////////////////////////////////////////////////////////

#include "stdafx.h"
#include <cmath>
#include "bts_DibExApi.hpp"

namespace bts
//////////////////////////////////////////////////////////////////////
{

// effect display
/************************************************************************* 
 * 
 * DrawTransparentBitmap () 
 * 
 * Parameters: 
 * 
 * HDC hDC          - DC to do output to 
 * 
 * HBITMAP hBitmap    - handle of bitmap to draw transparently
 * 
 * LONG xStart, yStart   - start position to draw *
 *
 * COLORREF cTransparentColor - transparent color * 
 * Return Value: 
 * 
 * Description: none
 *		 Draw bitmap transparently
 * 
 ************************************************************************/ 
void DrawTransparentBitmap(HDC hdc, HBITMAP hBitmap, 
						   LONG xStart, LONG yStart, 
						   COLORREF cTransparentColor)
{
   BITMAP     bm;
   COLORREF   cColor;
   HBITMAP    bmAndBack, bmAndObject, bmAndMem, bmSave;
   HBITMAP    bmBackOld, bmObjectOld, bmMemOld, bmSaveOld;
   HDC        hdcMem, hdcBack, hdcObject, hdcTemp, hdcSave;
   POINT      ptSize;

   hdcTemp = CreateCompatibleDC(hdc);
   SelectObject(hdcTemp, hBitmap);   // Select the bitmap

   GetObject(hBitmap, sizeof(BITMAP), (LPBYTE)&bm);
   ptSize.x = bm.bmWidth;            // Get width of bitmap
   ptSize.y = bm.bmHeight;           // Get height of bitmap
   DPtoLP(hdcTemp, &ptSize, 1);      // Convert from device
                                     // to logical points

   // Create some DCs to hold temporary data.
   hdcBack   = CreateCompatibleDC(hdc);
   hdcObject = CreateCompatibleDC(hdc);
   hdcMem    = CreateCompatibleDC(hdc);
   hdcSave   = CreateCompatibleDC(hdc);

   // Create a bitmap for each DC. DCs are required for a number of
   // GDI functions.

   // Monochrome DC
   bmAndBack   = CreateBitmap(ptSize.x, ptSize.y, 1, 1, NULL);

   // Monochrome DC
   bmAndObject = CreateBitmap(ptSize.x, ptSize.y, 1, 1, NULL);

   bmAndMem    = CreateCompatibleBitmap(hdc, ptSize.x, ptSize.y);
   bmSave      = CreateCompatibleBitmap(hdc, ptSize.x, ptSize.y);

   // Each DC must select a bitmap object to store pixel data.
   bmBackOld   = (HBITMAP)SelectObject(hdcBack, bmAndBack);
   bmObjectOld = (HBITMAP)SelectObject(hdcObject, bmAndObject);
   bmMemOld    = (HBITMAP)SelectObject(hdcMem, bmAndMem);
   bmSaveOld   = (HBITMAP)SelectObject(hdcSave, bmSave);

   // Set proper mapping mode.
   SetMapMode(hdcTemp, GetMapMode(hdc));

   // Save the bitmap sent here, because it will be overwritten.
   BitBlt(hdcSave, 0, 0, ptSize.x, ptSize.y, hdcTemp, 0, 0, SRCCOPY);

   // Set the background color of the source DC to the color.
   // contained in the parts of the bitmap that should be transparent
   cColor = SetBkColor(hdcTemp, cTransparentColor);

   // Create the object mask for the bitmap by performing a BitBlt
   // from the source bitmap to a monochrome bitmap.
   BitBlt(hdcObject, 0, 0, ptSize.x, ptSize.y, hdcTemp, 0, 0,
          SRCCOPY);

   // Set the background color of the source DC back to the original
   // color.
   SetBkColor(hdcTemp, cColor);

   // Create the inverse of the object mask.
   BitBlt(hdcBack, 0, 0, ptSize.x, ptSize.y, hdcObject, 0, 0,
          NOTSRCCOPY);

   // Copy the background of the main DC to the destination.
   BitBlt(hdcMem, 0, 0, ptSize.x, ptSize.y, hdc, xStart, yStart,
          SRCCOPY);

   // Mask out the places where the bitmap will be placed.
   BitBlt(hdcMem, 0, 0, ptSize.x, ptSize.y, hdcObject, 0, 0, SRCAND);

   // Mask out the transparent colored pixels on the bitmap.
   BitBlt(hdcTemp, 0, 0, ptSize.x, ptSize.y, hdcBack, 0, 0, SRCAND);

   // XOR the bitmap with the background on the destination DC.
   BitBlt(hdcMem, 0, 0, ptSize.x, ptSize.y, hdcTemp, 0, 0, SRCPAINT);

   // Copy the destination to the screen.
   BitBlt(hdc, xStart, yStart, ptSize.x, ptSize.y, hdcMem, 0, 0,
          SRCCOPY);

   // Place the original bitmap back into the bitmap sent here.
   BitBlt(hdcTemp, 0, 0, ptSize.x, ptSize.y, hdcSave, 0, 0, SRCCOPY);

   // Delete the memory bitmaps.
   DeleteObject(SelectObject(hdcBack, bmBackOld));
   DeleteObject(SelectObject(hdcObject, bmObjectOld));
   DeleteObject(SelectObject(hdcMem, bmMemOld));
   DeleteObject(SelectObject(hdcSave, bmSaveOld));

   // Delete the memory DCs.
   DeleteDC(hdcMem);
   DeleteDC(hdcBack);
   DeleteDC(hdcObject);
   DeleteDC(hdcSave);
   DeleteDC(hdcTemp);
}

/************************************************************************* 
 * 
 * Fade() 
 * 
 * Parameters: 
 * 
 * int nDeltaR   -  change value of Red
 * int nDeltaG   -  change value of Green
 * int nDeltaB   -  change value of Blue
 * BYTE rm		 -  maxinum value of Red
 * BYTE gm		 -  maxinum value of Green
 * BYTE bm		 -  maxinum value of Blue
 * BYTE *r		 -  pointer of value of Red after change
 * BYTE *g		 -  pointer of value of Green after change
 * BYTE *b		 -  pointer of value of Blue after change
 * 
 * Return Value: 
 *
 * BOOL          -  TRUE if success, FALSE is failue
 * 
 * Description: 
 * 
 * This function change the value of R, G, b
 * 
 ************************************************************************/
BOOL Fade(int nDeltaR, int nDeltaG, int nDeltaB, 
		  BYTE rm, BYTE gm, BYTE bm,
		  BYTE *r, BYTE *g, BYTE *b)
{
	int R = *r + nDeltaR;
	int G = *g + nDeltaG;
	int B = *b + nDeltaB;

	if (nDeltaR < 0)
		*r = (BYTE)BOUND(R, rm, 255);
	else
		*r = (BYTE)BOUND(R, 0, rm);
	if (nDeltaG < 0)
		*g = (BYTE)BOUND(G, gm, 255);
	else
		*g = (BYTE)BOUND(G, 0, gm);
	if (nDeltaB < 0)
		*b = (BYTE)BOUND(B, bm, 255);
	else
		*b = (BYTE)BOUND(B, 0, bm);

	if (*r==rm && *g==gm && *b==bm)
		return TRUE;
	else
		return FALSE;
}
	
// DIB transform
/************************************************************************* 
 * 
 * CropBitmap() 
 * 
 * Parameters: 
 * 
 * HBITMAP hBitmap - handle of DDB to be cropped
 * LPRECT lpRect   - specified rectangle to crop
 * 
 * Return Value: 
 * 
 * HDIB             - Handle to new bitmap 
 * 
 * Description: 
 * 
 * This function crop the specified rectangle in DDB, and return
 * the cropped DDB in a new DIB handle, let the source DDB unchanged
 * 
 ************************************************************************/ 
HBITMAP CropBitmap(HBITMAP hBitmap, LPRECT lpRect)
{
	WaitCursorBegin();

	// Get Curent DC
	HDC hDC = GetDC(NULL);
	// create bitmap
	HBITMAP hNewBmp = CreateCompatibleBitmap(hDC, RECTWIDTH(lpRect), RECTHEIGHT(lpRect));
	if (hNewBmp == NULL)
	{
		ReleaseDC(NULL, hDC);
		WaitCursorEnd();
		return NULL;
	}
	// Create memory DC
	HDC hMemDC = CreateCompatibleDC(hDC);
	if (hMemDC == NULL)
	{
		ReleaseDC(NULL, hDC);
		DeleteObject(hNewBmp);
		WaitCursorEnd();
		return NULL;
	}
	HDC hSrcMemDC = CreateCompatibleDC(hDC);
	if (hSrcMemDC == NULL)
	{
		DeleteDC(hMemDC);
		ReleaseDC(NULL, hDC);
		DeleteObject(hNewBmp);
		WaitCursorEnd();
		return NULL;
	}

	// Select new bitmap in memory DC
	HBITMAP hOldBitmap = (HBITMAP)SelectObject(hMemDC, hNewBmp);
	// select old bitmap in SrcMemDC
	HBITMAP hSrcOldBitmap = (HBITMAP)SelectObject(hSrcMemDC, hBitmap);

	// bitblt old bitmap in new bitmap 
	if (! BitBlt(hMemDC, 
				 0, 
				 0,
				 RECTWIDTH(lpRect), 
				 RECTHEIGHT(lpRect),
				 hSrcMemDC,
				 lpRect->left,
				 lpRect->top,
				 SRCCOPY))
	{
		SelectObject(hMemDC, hOldBitmap);
		SelectObject(hSrcMemDC, hSrcOldBitmap);
		DeleteDC(hMemDC);
		DeleteDC(hSrcMemDC);
		ReleaseDC(NULL, hDC);
		DeleteObject(hNewBmp);
		WaitCursorEnd();
		return NULL;
	}
	
	// restore
	SelectObject(hMemDC, hOldBitmap);
	SelectObject(hSrcMemDC, hSrcOldBitmap);
	DeleteDC(hMemDC);
	DeleteDC(hSrcMemDC);
	ReleaseDC(NULL, hDC);
	WaitCursorEnd();

	// return
	return hNewBmp;
}

/************************************************************************* 
 * 
 * CropDIB() 
 * 
 * Parameters: 
 * 
 * HDIB hDIB       - handle of DIB to be cropped
 * LPRECT lpRect   - specified rectangle to crop
 * 
 * Return Value: 
 * 
 * HDIB             - Handle to new DIB 
 * 
 * Description: 
 * 
 * This function crop the specified rectangle in DIB, and return
 * the cropped DIB in a new DIB handle, let the source DIB unchanged
 * 
 ************************************************************************/ 
HDIB CropDIB(HDIB hDIB, LPRECT lpRect)
{
    LPBITMAPINFO lpbmi = NULL; 
    LPBYTE       lpSourceBits, lpTargetBits, lpResult; 
    HDC			 hDC = NULL, hSourceDC, hTargetDC; 
    HBITMAP      hSourceBitmap, hTargetBitmap, hOldTargetBitmap, hOldSourceBitmap; 
    DWORD        dwSourceBitsSize, dwTargetBitsSize, dwTargetHeaderSize; 
	int			 nWidth, nHeight;
	HDIB		 hNewDIB;
	DWORD		 dwSize;

	WaitCursorBegin();

	// Get DIB pointer
	if (! hDIB)
	{
		WaitCursorEnd();
		return NULL;
	}
	LPBITMAPINFO lpSrcDIB = (LPBITMAPINFO)GlobalLock(hDIB);
	if (! lpSrcDIB)
	{
		WaitCursorEnd();
		return NULL;
	}

    // Allocate and fill out a BITMAPINFO struct for the new DIB 
    dwTargetHeaderSize = sizeof( BITMAPINFOHEADER ) + PaletteSize(lpSrcDIB); 
    lpbmi = (LPBITMAPINFO)malloc( dwTargetHeaderSize ); 
	memcpy(lpbmi, lpSrcDIB, dwTargetHeaderSize);
	nWidth = RECTWIDTH(lpRect);
	nHeight = RECTHEIGHT(lpRect);
    lpbmi->bmiHeader.biWidth = nWidth; 
    lpbmi->bmiHeader.biHeight = nHeight; 
 
    // Gonna use DIBSections and BitBlt() to do the conversion, so make 'em 
	hDC = GetDC( NULL ); 
    hTargetBitmap = CreateDIBSection( hDC, lpbmi, DIB_RGB_COLORS, (VOID **)&lpTargetBits, NULL, 0 ); 
    hSourceBitmap = CreateDIBSection( hDC, lpSrcDIB, DIB_RGB_COLORS, (VOID **)&lpSourceBits, NULL, 0 ); 
    hSourceDC = CreateCompatibleDC( hDC ); 
    hTargetDC = CreateCompatibleDC( hDC ); 
 
    // Flip the bits on the source DIBSection to match the source DIB 
    dwSourceBitsSize = lpSrcDIB->bmiHeader.biHeight * BytesPerLine((LPBYTE)&(lpSrcDIB->bmiHeader)); 
    dwTargetBitsSize = lpbmi->bmiHeader.biHeight * BytesPerLine((LPBYTE)&(lpbmi->bmiHeader)); 
    memcpy( lpSourceBits, FindDIBBits((LPBYTE)lpSrcDIB), dwSourceBitsSize ); 
    lpbmi->bmiHeader.biSizeImage = dwTargetBitsSize; 
 
    // Select DIBSections into DCs 
    hOldSourceBitmap = (HBITMAP)SelectObject( hSourceDC, hSourceBitmap ); 
    hOldTargetBitmap = (HBITMAP)SelectObject( hTargetDC, hTargetBitmap ); 
 
	// put old bitmap in new bitmap 
    BitBlt(hTargetDC, 0, 0, nWidth, nHeight, hSourceDC, lpRect->left, lpRect->top, SRCCOPY); 
 
    // Clean up and delete the DCs 
    SelectObject( hSourceDC, hOldSourceBitmap ); 
    SelectObject( hTargetDC, hOldTargetBitmap ); 
    DeleteDC( hSourceDC ); 
    DeleteDC( hTargetDC ); 
    ReleaseDC( NULL, hDC ); 
 
    // Flush the GDI batch, so we can play with the bits 
    GdiFlush(); 
 
    // Allocate enough memory for the new CF_DIB, and copy bits 
	dwSize = dwTargetHeaderSize + dwTargetBitsSize;
	hNewDIB = GlobalAlloc(GHND, dwSize);
    lpResult = (LPBYTE)GlobalLock(hNewDIB);//malloc( dwTargetHeaderSize + dwTargetBitsSize ); 
    memcpy( lpResult, lpbmi, dwTargetHeaderSize ); 
    memcpy( FindDIBBits( (LPBYTE)lpResult ), lpTargetBits, dwTargetBitsSize ); 
 
    // final cleanup 
    DeleteObject( hTargetBitmap ); 
    DeleteObject( hSourceBitmap ); 
    free( lpbmi ); 
	GlobalUnlock(hDIB);
	GlobalUnlock(hNewDIB);
	WaitCursorEnd();
 
    return hNewDIB;
}

/************************************************************************* 
 * 
 * CutDIB() 
 * 
 * Parameters: 
 * 
 * HDIB hDIB       - handle of DIB to be cut
 * LPRECT lpRect   - specified rectangle to cut off
 * 
 * Return Value: 
 * 
 * HDIB             - Handle to new DIB 
 * 
 * Description: 
 * 
 * This function cut off the specified rectangle in DIB, and return
 * the cut DIB in a new DIB handle, let the source DIB unchanged
 * 
 ************************************************************************/ 
HDIB CutDIB(HDIB hDIB, LPRECT lpRect)
{
    LPBITMAPINFO lpbmi = NULL; 
    LPBYTE       lpTargetBits; 
    HDC			 hDC = NULL, hTargetDC; 
    HBITMAP      hTargetBitmap, hOldTargetBitmap; 
    DWORD        dwTargetBitsSize; 
	HDIB		 hNewDIB;

	WaitCursorBegin();

	// Get new DIB handle
	if (! hDIB)
	{
		WaitCursorEnd();
		return NULL;
	}
	hNewDIB = CopyHandle(hDIB);
	if (! hNewDIB)
	{
		WaitCursorEnd();
		return NULL;
	}

    // Gonna use DIBSections and BitBlt() to do the conversion, so make 'em 
	hDC = GetDC( NULL ); 
    lpbmi = (LPBITMAPINFO)GlobalLock(hNewDIB); 
    hTargetBitmap = CreateDIBSection( hDC, lpbmi, DIB_RGB_COLORS, (VOID **)&lpTargetBits, NULL, 0 ); 
    hTargetDC = CreateCompatibleDC( hDC ); 
 
    // Flip the bits on the source DIBSection to match the source DIB 
    dwTargetBitsSize = lpbmi->bmiHeader.biHeight * BytesPerLine((LPBYTE)&(lpbmi->bmiHeader)); 
    memcpy( lpTargetBits, FindDIBBits((LPBYTE)lpbmi), dwTargetBitsSize ); 

    // Select DIBSections into DCs 
    hOldTargetBitmap = (HBITMAP)SelectObject( hTargetDC, hTargetBitmap ); 
 
	// clear rectangle specified
	HBRUSH hBrush = CreateSolidBrush(RGB(255,255,255));
	HPEN hPen = CreatePen(PS_SOLID, 1, RGB(255,255,255));
	HBRUSH hOldBrush = (HBRUSH)SelectObject(hTargetDC, hBrush);
	HPEN hOldPen = (HPEN)SelectObject(hTargetDC, hPen);
	Rectangle(hTargetDC, lpRect->left, lpRect->top, lpRect->right, lpRect->bottom);
	SelectObject(hTargetDC, hOldBrush);
	SelectObject(hTargetDC, hOldPen);
 
    // Clean up and delete the DCs 
    SelectObject( hTargetDC, hOldTargetBitmap ); 
    DeleteDC( hTargetDC ); 
    ReleaseDC( NULL, hDC ); 
 
    // Flush the GDI batch, so we can play with the bits 
    GdiFlush(); 
 
    // copy bits 
    memcpy( FindDIBBits( (LPBYTE)lpbmi ), lpTargetBits, dwTargetBitsSize ); 
 
    // final cleanup 
    DeleteObject( hTargetBitmap ); 
	GlobalUnlock(hNewDIB);
	WaitCursorEnd();
 
    return hNewDIB;
}

/************************************************************************* 
 * 
 * MergeDIB() 
 * 
 * Parameters: 
 * 
 * HDIB hDIB1       - handle of DIB to be merged to
 * HDIB hDIB2       - handle of DIB to be merged from
 * POINT ptTopLeft  - start merge position in hDIB1
 * 
 * Return Value: 
 * 
 * HDIB             - Handle to new DIB 
 * 
 * Description: 
 * 
 * This function merge the second DIB to the first DIB, and return
 * the merged DIB in a new DIB handle, let the source DIBs unchanged
 * 
 ************************************************************************/ 
HDIB MergeDIB(HDIB hDIB1, HDIB hDIB2, POINT ptTopLeft)
{
    LPBITMAPINFO lpbmi = NULL; 
    LPBYTE       lpSourceBits, lpTargetBits; 
    HDC			 hDC = NULL, hSourceDC, hTargetDC; 
    HBITMAP      hSourceBitmap, hTargetBitmap, hOldTargetBitmap, hOldSourceBitmap; 
    DWORD        dwTargetBitsSize, dwSourceBitsSize; 
	HDIB		 hNewDIB;
	int			 nWidth, nHeight;

	WaitCursorBegin();

	// Get DIB pointer
	if (! hDIB1 || ! hDIB2)
	{
		WaitCursorEnd();
		return NULL;
	}

	// new DIB
	hNewDIB = CopyHandle(hDIB1);
	if (! hNewDIB)
	{
		WaitCursorEnd();
		return NULL;
	}
    // New DIB buffer
    lpbmi = (LPBITMAPINFO)GlobalLock(hNewDIB);
	if (! lpbmi)
	{
		WaitCursorBegin();
		return NULL;
	}
	// DIB2 buffer
	LPBITMAPINFO lpSrcDIB = (LPBITMAPINFO)GlobalLock(hDIB2);
	
	if (! lpSrcDIB)
	{
		GlobalUnlock(hNewDIB);
		DestroyDIB(hNewDIB);
		WaitCursorBegin();
		return NULL;
	}
	nWidth = DIBWidth(lpSrcDIB);
	nHeight = DIBHeight(lpSrcDIB);

    // Gonna use DIBSections and BitBlt() to do the conversion, so make 'em 
	hDC = GetDC( NULL ); 
    hTargetBitmap = CreateDIBSection( hDC, lpbmi, DIB_RGB_COLORS, (VOID **)&lpTargetBits, NULL, 0 ); 
    hSourceBitmap = CreateDIBSection( hDC, lpSrcDIB, DIB_RGB_COLORS, (VOID **)&lpSourceBits, NULL, 0 ); 
    hSourceDC = CreateCompatibleDC( hDC ); 
    hTargetDC = CreateCompatibleDC( hDC ); 
 
    // Flip the bits on the source DIBSection to match the source DIB 
    dwSourceBitsSize = lpSrcDIB->bmiHeader.biHeight * BytesPerLine((LPBYTE)&(lpSrcDIB->bmiHeader)); 
    dwTargetBitsSize = lpbmi->bmiHeader.biHeight * BytesPerLine((LPBYTE)&(lpbmi->bmiHeader)); 
    memcpy( lpSourceBits, FindDIBBits((LPBYTE)lpSrcDIB), dwSourceBitsSize ); 
    memcpy( lpTargetBits, FindDIBBits((LPBYTE)lpbmi), dwTargetBitsSize ); 

    // Select DIBSections into DCs 
    hOldSourceBitmap = (HBITMAP)SelectObject( hSourceDC, hSourceBitmap ); 
    hOldTargetBitmap = (HBITMAP)SelectObject( hTargetDC, hTargetBitmap ); 
 
	// Merge
    BitBlt(hTargetDC, ptTopLeft.x, ptTopLeft.y, nWidth, nHeight, hSourceDC, 0, 0, SRCCOPY); 
 
    // Clean up and delete the DCs 
    SelectObject( hSourceDC, hOldSourceBitmap ); 
    SelectObject( hTargetDC, hOldTargetBitmap ); 
    DeleteDC( hSourceDC ); 
    DeleteDC( hTargetDC ); 
    ReleaseDC( NULL, hDC ); 
 
    // Flush the GDI batch, so we can play with the bits 
    GdiFlush(); 
 
    // and copy bits 
    memcpy( FindDIBBits((LPBYTE)lpbmi), lpTargetBits, dwTargetBitsSize ); 
 
    // final cleanup 
    DeleteObject( hTargetBitmap ); 
    DeleteObject( hSourceBitmap ); 
	GlobalUnlock(hDIB2);
	GlobalUnlock(hNewDIB);
	WaitCursorEnd();
 
    return hNewDIB;
}

/************************************************************************* 
 * 
 * TransparentMergeDIB() 
 * 
 * Parameters: 
 * 
 * HDIB hDIB1       - handle of DIB to be merged to
 * HDIB hDIB2       - handle of DIB to be merged from
 * POINT ptTopLeft  - start merge position in hDIB1
 * COLORREF crTransparent - transparent color
 * 
 * Return Value: 
 * 
 * HDIB             - Handle to new DIB 
 * 
 * Description: 
 * 
 * This function merge the second DIB to the first DIB with transparent of specified
 * color, and return the merged DIB in a new DIB handle, let the source DIBs unchanged
 * 
 ************************************************************************/ 
HDIB TransparentMergeDIB(HDIB hDIB1, HDIB hDIB2, POINT ptTopLeft, COLORREF crTransparent)
{
    LPBITMAPINFO lpbmi = NULL; 
    LPBYTE       lpTargetBits; 
    HDC			 hDC = NULL, hTargetDC; 
    HBITMAP      hSourceBitmap, hTargetBitmap, hOldTargetBitmap; 
    DWORD        dwTargetBitsSize; 
	HDIB		 hNewDIB;
	int			 nWidth, nHeight;

	// Get DIB pointer
	if (! hDIB1 || ! hDIB2)
		return NULL;

	// new DIB
	hNewDIB = CopyHandle(hDIB1);
	if (! hNewDIB)
		return NULL;

    // New DIB buffer
    lpbmi = (LPBITMAPINFO)GlobalLock(hNewDIB);
	if (! lpbmi)
		return NULL;

	// DIB2 
	hSourceBitmap = DIBToBitmap(hDIB2, NULL);
	if (! hSourceBitmap)
	{
		GlobalUnlock(hNewDIB);
		DestroyDIB(hNewDIB);
		return NULL;
	}
	BITMAP bm;
	GetObject(hSourceBitmap, sizeof(BITMAP), &bm);
	nWidth = bm.bmWidth;
	nHeight = bm.bmHeight;

    // Gonna use DIBSections and BitBlt() to do the conversion, so make 'em 
	hDC = GetDC( NULL ); 
    hTargetBitmap = CreateDIBSection( hDC, lpbmi, DIB_RGB_COLORS, (VOID **)&lpTargetBits, NULL, 0 ); 
    hTargetDC = CreateCompatibleDC( hDC ); 
 
    // Flip the bits on the source DIBSection to match the source DIB 
    dwTargetBitsSize = lpbmi->bmiHeader.biHeight * BytesPerLine((LPBYTE)&(lpbmi->bmiHeader)); 
    memcpy( lpTargetBits, FindDIBBits((LPBYTE)lpbmi), dwTargetBitsSize ); 

    // Select DIBSections into DCs 
    hOldTargetBitmap = (HBITMAP)SelectObject( hTargetDC, hTargetBitmap ); 
    // Set the color tables for the DIBSections 
    if( lpbmi->bmiHeader.biBitCount <= 8 ) 
        SetDIBColorTable( hTargetDC, 0, 1 << lpbmi->bmiHeader.biBitCount, lpbmi->bmiColors ); 
 
	// Merge
	DrawTransparentBitmap(hTargetDC, hSourceBitmap, ptTopLeft.x, ptTopLeft.y, crTransparent);

    // Clean up and delete the DCs 
    SelectObject( hTargetDC, hOldTargetBitmap ); 
    DeleteDC( hTargetDC ); 
    ReleaseDC( NULL, hDC ); 
 
    // Flush the GDI batch, so we can play with the bits 
    GdiFlush(); 
 
    // and copy bits 
    memcpy( FindDIBBits((LPBYTE)lpbmi), lpTargetBits, dwTargetBitsSize ); 
 
    // final cleanup 
    DeleteObject( hTargetBitmap ); 
    DeleteObject( hSourceBitmap ); 
	GlobalUnlock(hDIB2);
	GlobalUnlock(hNewDIB);
 
    return hNewDIB;
}

// RotateBitmapNT	- Create a new bitmap with rotated image
// Returns		- Returns new bitmap with rotated image
// hBitmap		- Bitmap to rotate
// fDegrees		- Angle of rotation in degree
// clrBack		- Color of pixels in the resulting bitmap that do
//			  not get covered by source pixels
HBITMAP RotateBitmapNT(HBITMAP hBitmap, double fDegrees, COLORREF clrBack)
{
	// Create a memory DC compatible with the display
	CDC sourceDC, destDC;
	sourceDC.CreateCompatibleDC( NULL );
	destDC.CreateCompatibleDC( NULL );

	// Get logical coordinates
	BITMAP bm;
	::GetObject( hBitmap, sizeof( bm ), &bm );
	
	// Convert angle degree to radians 
	#define PI		3.1415926
	double radians = (fDegrees/90.0)*(PI/2);

	// Compute the cosine and sine only once
	float cosine = (float)cos(radians);
	float sine = (float)sin(radians);

	// Compute dimensions of the resulting bitmap
	// First get the coordinates of the 3 corners other than origin
	int x1 = (int)(bm.bmHeight * sine);
	int y1 = (int)(bm.bmHeight * cosine);
	int x2 = (int)(bm.bmWidth * cosine + bm.bmHeight * sine);
	int y2 = (int)(bm.bmHeight * cosine - bm.bmWidth * sine);
	int x3 = (int)(bm.bmWidth * cosine);
	int y3 = (int)(-bm.bmWidth * sine);

	int minx = min(0,min(x1, min(x2,x3)));
	int miny = min(0,min(y1, min(y2,y3)));
	int maxx = max(0,max(x1, max(x2,x3)));
	int maxy = max(0,max(y1, max(y2,y3)));

	int w = maxx - minx;
	int h = maxy - miny;

	// Create a bitmap to hold the result
	HBITMAP hbmResult = ::CreateCompatibleBitmap(CClientDC(NULL), w, h);

	HBITMAP hbmOldSource = (HBITMAP)::SelectObject( sourceDC.m_hDC, hBitmap );
	HBITMAP hbmOldDest = (HBITMAP)::SelectObject( destDC.m_hDC, hbmResult );

	// Draw the background color before we change mapping mode
	HBRUSH hbrBack = CreateSolidBrush( clrBack );
	HBRUSH hbrOld = (HBRUSH)::SelectObject( destDC.m_hDC, hbrBack );
	destDC.PatBlt( 0, 0, w, h, PATCOPY );
	::DeleteObject( ::SelectObject( destDC.m_hDC, hbrOld ) );

	// We will use world transform to rotate the bitmap
	SetGraphicsMode(destDC.m_hDC, GM_ADVANCED);
	XFORM xform;
	xform.eM11 = cosine;
	xform.eM12 = -sine;
	xform.eM21 = sine;
	xform.eM22 = cosine;
	xform.eDx = (float)-minx;
	xform.eDy = (float)-miny;

	SetWorldTransform( destDC.m_hDC, &xform );

	// Now do the actual rotating - a pixel at a time
	destDC.BitBlt(0,0,bm.bmWidth, bm.bmHeight, &sourceDC, 0, 0, SRCCOPY );

	// Restore DCs
	::SelectObject( sourceDC.m_hDC, hbmOldSource );
	::SelectObject( destDC.m_hDC, hbmOldDest );

	return hbmResult;
}

// RotateBitmap	- Create a new bitmap with rotated image
// Returns		- Returns new bitmap with rotated image
// hBitmap		- Bitmap to rotate
// fDegrees		- Angle of rotation in degree
// clrBack		- Color of pixels in the resulting bitmap that do
//			  not get covered by source pixels
// Note			- If the bitmap uses colors not in the system palette 
//			  then the result is unexpected. You can fix this by
//			  adding an argument for the logical palette.
HBITMAP RotateBitmap(HBITMAP hBitmap, double fDegrees, COLORREF clrBack)
{
	// Create a memory DC compatible with the display
	CDC sourceDC, destDC;
	sourceDC.CreateCompatibleDC( NULL );
	destDC.CreateCompatibleDC( NULL );

	// Get logical coordinates
	BITMAP bm;
	::GetObject( hBitmap, sizeof( bm ), &bm );

	// Convert angle degree to radians 
	#define PI		3.1415926
	double radians = (fDegrees/90.0)*(PI/2);

	// Compute the cosine and sine only once
	float cosine = (float)cos(radians);
	float sine = (float)sin(radians);

	// Compute dimensions of the resulting bitmap
	// First get the coordinates of the 3 corners other than origin
	int x1 = (int)(-bm.bmHeight * sine);
	int y1 = (int)(bm.bmHeight * cosine);
	int x2 = (int)(bm.bmWidth * cosine - bm.bmHeight * sine);
	int y2 = (int)(bm.bmHeight * cosine + bm.bmWidth * sine);
	int x3 = (int)(bm.bmWidth * cosine);
	int y3 = (int)(bm.bmWidth * sine);

	int minx = min(0,min(x1, min(x2,x3)));
	int miny = min(0,min(y1, min(y2,y3)));
	int maxx = max(x1, max(x2,x3));
	int maxy = max(y1, max(y2,y3));

	int w = maxx - minx;
	int h = maxy - miny;


	// Create a bitmap to hold the result
	HBITMAP hbmResult = ::CreateCompatibleBitmap(CClientDC(NULL), w, h);

	HBITMAP hbmOldSource = (HBITMAP)::SelectObject( sourceDC.m_hDC, hBitmap );
	HBITMAP hbmOldDest = (HBITMAP)::SelectObject( destDC.m_hDC, hbmResult );

	// Draw the background color before we change mapping mode
	HBRUSH hbrBack = CreateSolidBrush( clrBack );
	HBRUSH hbrOld = (HBRUSH)::SelectObject( destDC.m_hDC, hbrBack );
	destDC.PatBlt( 0, 0, w, h, PATCOPY );
	::DeleteObject( ::SelectObject( destDC.m_hDC, hbrOld ) );

	// Set mapping mode so that +ve y axis is upwords
	sourceDC.SetMapMode(MM_ISOTROPIC);
	sourceDC.SetWindowExt(1,1);
	sourceDC.SetViewportExt(1,-1);
	sourceDC.SetViewportOrg(0, bm.bmHeight-1);

	destDC.SetMapMode(MM_ISOTROPIC);
	destDC.SetWindowExt(1,1);
	destDC.SetViewportExt(1,-1);
	destDC.SetWindowOrg(minx, maxy);

	// Now do the actual rotating - a pixel at a time
	// Computing the destination point for each source point
	// will leave a few pixels that do not get covered
	// So we use a reverse transform - e.i. compute the source point
	// for each destination point

	for( int y = miny; y < maxy; y++ )
	{
		for( int x = minx; x < maxx; x++ )
		{
			int sourcex = (int)(x*cosine + y*sine);
			int sourcey = (int)(y*cosine - x*sine);
			if( sourcex >= 0 && sourcex < bm.bmWidth && sourcey >= 0 
					&& sourcey < bm.bmHeight )
				destDC.SetPixel(x,y,sourceDC.GetPixel(sourcex,sourcey));
		}
	}

	// Restore DCs
	::SelectObject( sourceDC.m_hDC, hbmOldSource );
	::SelectObject( destDC.m_hDC, hbmOldDest );

	return hbmResult;
}

// RotateDIB	- Create a new bitmap with rotated image
// Returns		- Returns new bitmap with rotated image
// hDIB			- Device-independent bitmap to rotate
// fDegrees		- Angle of rotation in degree
// clrBack		- Color of pixels in the resulting bitmap that do
//			  not get covered by source pixels
HDIB RotateDIB(HDIB hDIB, double fDegrees, COLORREF clrBack)
{
	WaitCursorBegin();

	// Get source bitmap info
	LPBITMAPINFO lpBmInfo = (LPBITMAPINFO)GlobalLock(hDIB);
	int bpp = lpBmInfo->bmiHeader.biBitCount;		// Bits per pixel
	
	int nColors = lpBmInfo->bmiHeader.biClrUsed ? lpBmInfo->bmiHeader.biClrUsed : 
					1 << bpp;
	int nWidth = lpBmInfo->bmiHeader.biWidth;
	int nHeight = lpBmInfo->bmiHeader.biHeight;
	int nRowBytes = ((((nWidth * bpp) + 31) & ~31) / 8);

	// Make sure height is positive and biCompression is BI_RGB or BI_BITFIELDS
	DWORD compression = lpBmInfo->bmiHeader.biCompression;
	if( nHeight < 0 || (compression!=BI_RGB))
	{
		GlobalUnlock(hDIB);
		WaitCursorEnd();
		return NULL;
	}

	LPBYTE lpDIBBits = FindDIBBits((LPBYTE)lpBmInfo);
    
	// Convert angle degree to radians 
	#define PI		3.1415926
	double radians = (fDegrees/90.0)*(PI/2);

	// Compute the cosine and sine only once
	float cosine = (float)cos(radians);
	float sine = (float)sin(radians);

	// Compute dimensions of the resulting bitmap
	// First get the coordinates of the 3 corners other than origin
	int x1 = (int)(-nHeight * sine);
	int y1 = (int)(nHeight * cosine);
	int x2 = (int)(nWidth * cosine - nHeight * sine);
	int y2 = (int)(nHeight * cosine + nWidth * sine);
	int x3 = (int)(nWidth * cosine);
	int y3 = (int)(nWidth * sine);

	int minx = min(0,min(x1, min(x2,x3)));
	int miny = min(0,min(y1, min(y2,y3)));
	int maxx = max(x1, max(x2,x3));
	int maxy = max(y1, max(y2,y3));

	int w = maxx - minx;
	int h = maxy - miny;

	// Create a DIB to hold the result
	int nResultRowBytes = ((((w * bpp) + 31) & ~31) / 8);
	long len = nResultRowBytes * h;
	int nHeaderSize = ((LPBYTE)lpDIBBits-(LPBYTE)lpBmInfo) ;
	HANDLE hDIBResult = GlobalAlloc(GHND,len+nHeaderSize);
	// Initialize the header information
	LPBITMAPINFO lpBmInfoResult = (LPBITMAPINFO)GlobalLock(hDIBResult);
	memcpy( (void*)lpBmInfoResult, (void*)lpBmInfo, nHeaderSize);
	lpBmInfoResult->bmiHeader.biWidth = w;
	lpBmInfoResult->bmiHeader.biHeight = h;
	lpBmInfoResult->bmiHeader.biSizeImage = len;

	LPBYTE lpDIBBitsResult = FindDIBBits((LPBYTE)lpBmInfoResult);

	// Get the back color value (index)
	ZeroMemory( lpDIBBitsResult, len );
	DWORD dwBackColor;
	switch(bpp)
	{
	case 1:	//Monochrome
		if( clrBack == RGB(255,255,255) )
			memset( lpDIBBitsResult, 0xff, len );
		break;
	case 4:
	case 8:	//Search the color table
		int i;
		for(i = 0; i < nColors; i++ )
		{
			if( lpBmInfo->bmiColors[i].rgbBlue ==  GetBValue(clrBack)
				&& lpBmInfo->bmiColors[i].rgbGreen ==  GetGValue(clrBack)
				&& lpBmInfo->bmiColors[i].rgbRed ==  GetRValue(clrBack) )
			{
				if(bpp==4) i = i | i<<4;
				memset( lpDIBBitsResult, i, len );
				break;
			}
		}
		// If not match found the color remains black
		break;
	case 16:
		// Windows95 supports 5 bits each for all colors or 5 bits for red & blue
		// and 6 bits for green - Check the color mask for RGB555 or RGB565
		if( *((DWORD*)lpBmInfo->bmiColors) == 0x7c00 )
		{
			// Bitmap is RGB555
			dwBackColor = ((GetRValue(clrBack)>>3) << 10) + 
					((GetRValue(clrBack)>>3) << 5) +
					(GetBValue(clrBack)>>3) ;
		}
		else
		{
			// Bitmap is RGB565
			dwBackColor = ((GetRValue(clrBack)>>3) << 11) + 
					((GetRValue(clrBack)>>2) << 5) +
					(GetBValue(clrBack)>>3) ;
		}
		break;
	case 24:
	case 32:
		dwBackColor = (((DWORD)GetRValue(clrBack)) << 16) | 
				(((DWORD)GetGValue(clrBack)) << 8) |
				(((DWORD)GetBValue(clrBack)));
		break;
	}


	// Now do the actual rotating - a pixel at a time
	// Computing the destination point for each source point
	// will leave a few pixels that do not get covered
	// So we use a reverse transform - e.i. compute the source point
	// for each destination point

	for( int y = 0; y < h; y++ )
	{
		for( int x = 0; x < w; x++ )
		{
			int sourcex = (int)((x+minx)*cosine + (y+miny)*sine);
			int sourcey = (int)((y+miny)*cosine - (x+minx)*sine);
			if( sourcex >= 0 && sourcex < nWidth && sourcey >= 0 
				&& sourcey < nHeight )
			{
				// Set the destination pixel
				switch(bpp)
				{
					BYTE mask;
				case 1:		//Monochrome
					mask = *((LPBYTE)lpDIBBits + nRowBytes*sourcey + 
						sourcex/8) & (0x80 >> sourcex%8);
					//Adjust mask for destination bitmap
					mask = mask ? (0x80 >> x%8) : 0;
					*((LPBYTE)lpDIBBitsResult + nResultRowBytes*(y) + 
								(x/8)) &= ~(0x80 >> x%8);
					*((LPBYTE)lpDIBBitsResult + nResultRowBytes*(y) + 
								(x/8)) |= mask;
					break;
				case 4:
					mask = *((LPBYTE)lpDIBBits + nRowBytes*sourcey + 
						sourcex/2) & ((sourcex&1) ? 0x0f : 0xf0);
					//Adjust mask for destination bitmap
					if( (sourcex&1) != (x&1) )
						mask = (mask&0xf0) ? (mask>>4) : (mask<<4);
					*((LPBYTE)lpDIBBitsResult + nResultRowBytes*(y) + 
							(x/2)) &= ~((x&1) ? 0x0f : 0xf0);
					*((LPBYTE)lpDIBBitsResult + nResultRowBytes*(y) + 
							(x/2)) |= mask;
					break;
				case 8:
					BYTE pixel ;
					pixel = *((LPBYTE)lpDIBBits + nRowBytes*sourcey + 
							sourcex);
					*((LPBYTE)lpDIBBitsResult + nResultRowBytes*(y) + 
							(x)) = pixel;
					break;
				case 16:
					DWORD dwPixel;
					dwPixel = *((LPWORD)((LPBYTE)lpDIBBits + 
							nRowBytes*sourcey + sourcex*2));
					*((LPWORD)((LPBYTE)lpDIBBitsResult + 
						nResultRowBytes*y + x*2)) = (WORD)dwPixel;
					break;
				case 24:
					dwPixel = *((LPDWORD)((LPBYTE)lpDIBBits + 
						nRowBytes*sourcey + sourcex*3)) & 0xffffff;
					*((LPDWORD)((LPBYTE)lpDIBBitsResult + 
						nResultRowBytes*y + x*3)) |= dwPixel;
					break;
				case 32:
					dwPixel = *((LPDWORD)((LPBYTE)lpDIBBits + 
						nRowBytes*sourcey + sourcex*4));
					*((LPDWORD)((LPBYTE)lpDIBBitsResult + 
						nResultRowBytes*y + x*4)) = dwPixel;
				}
			}
			else 
			{
				// Draw the background color. The background color
				// has already been drawn for 8 bits per pixel and less
				switch(bpp)
				{
				case 16:
					*((LPWORD)((LPBYTE)lpDIBBitsResult + 
						nResultRowBytes*y + x*2)) = 
						(WORD)dwBackColor;
					break;
				case 24:
					*((LPDWORD)((LPBYTE)lpDIBBitsResult + 
						nResultRowBytes*y + x*3)) |= dwBackColor;
					break;
				case 32:
					*((LPDWORD)((LPBYTE)lpDIBBitsResult + 
						nResultRowBytes*y + x*4)) = dwBackColor;
					break;
				}
			}
		}
	}

	GlobalUnlock(hDIB);
	GlobalUnlock(hDIBResult);
	WaitCursorEnd();

	return hDIBResult;
}

/************************************************************************* 
 * 
 * RotateDIB() 
 * 
 * Parameters: 
 * 
 * HDIB hDIB				 - handle of DIB to rotate
 * 
 * Return Value: 
 * 
 * HDIB             - Handle to new DIB 
 * 
 * Description: 
 * 
 * This function rotate DIB 90 degree counter clockwise, and return
 * the rotated DIB in a new DIB handle, let the source DIB unchanged
 * 
 ************************************************************************/ 
HDIB RotateDIB(HDIB hDib)
{
	WaitCursorBegin();

	// old DIB
	LPBYTE lpDIBSrc = (LPBYTE)GlobalLock(hDib);

	DWORD lSrcWidth = DIBWidth(lpDIBSrc);
	DWORD lSrcHeight = DIBHeight(lpDIBSrc);
	WORD wBitCount = ((LPBITMAPINFOHEADER)lpDIBSrc)->biBitCount;
	// bits position
    LPBYTE lpOldBits = FindDIBBits(lpDIBSrc);

	// get bytes/pixel, bytes/row of new DIB
	double fColorBytes = (double)((double)wBitCount/8.0);
	DWORD lSrcRowBytes = WIDTHBYTES(lSrcWidth*((DWORD)wBitCount));
	DWORD lDestRowBytes = WIDTHBYTES(lSrcHeight*((DWORD)wBitCount));

	// adjust new DIB size
	DWORD dwDataLength = GlobalSize(hDib);
	dwDataLength += lDestRowBytes*(lSrcWidth-1)+(DWORD)((lSrcHeight-1)*fColorBytes) - 
				  lSrcRowBytes*(lSrcHeight-1)+(DWORD)((lSrcWidth-1)*fColorBytes);
	HDIB hNewDib = GlobalAlloc(GHND, dwDataLength);
	if (! hNewDib)
	{
		WaitCursorEnd();
		return NULL;
	}
	// new DIB buffer
	LPBYTE lpDIB = (LPBYTE)GlobalLock(hNewDib);
	// copy LPBITMAPINFO from old to new
	memcpy(lpDIB, lpDIBSrc, sizeof(BITMAPINFOHEADER)+PaletteSize(lpDIBSrc));
	// swap width and height
	((LPBITMAPINFOHEADER)lpDIB)->biHeight = lSrcWidth;
	((LPBITMAPINFOHEADER)lpDIB)->biWidth = lSrcHeight;
	// new bits position
	LPBYTE lpData = FindDIBBits(lpDIB);

	// trandform bits
	DWORD i, j;
	switch (wBitCount)
	{
	case 1:
		for (i=0; i<lSrcHeight; ++i)
		{
			for (j=0; j<lSrcWidth; ++j)
			{
				*(lpData+(lDestRowBytes*j+(lSrcHeight-i-1)/8)) &= ~(1<<(7-((lSrcHeight-i-1)%8)));
				*(lpData+(lDestRowBytes*j+(lSrcHeight-i-1)/8)) |= 
					((*(lpOldBits+(lSrcRowBytes*i+j/8))<<(j%8))>>7)<<(7-((lSrcHeight-i-1)%8));
			}
		}
		break;
	case 4:
		for (i=0; i<lSrcHeight; ++i)
		{
			for (j=0; j<lSrcWidth; ++j)
			{
				*(lpData+(lDestRowBytes*j+(lSrcHeight-i-1)/2)) &= ((lSrcHeight-i-1)%2) ? 0xf0 : 0x0f;
				*(lpData+(lDestRowBytes*j+(lSrcHeight-i-1)/2)) |= 
					((*(lpOldBits+(lSrcRowBytes*i+j/2))<<(j%2 ? 4 : 0))>>4)<<(((lSrcHeight-i-1)%2) ? 0 : 4);
			}
		}
		break;
	case 8:
		for (i=0; i<lSrcHeight; ++i)
		{
			for (j=0; j<lSrcWidth; ++j)
			{
				*(lpData+(lDestRowBytes*j+lSrcHeight-i-1))
					= *(lpOldBits+(lSrcRowBytes*i+j));
			}
		}
		break;
	case 24:
		for (i=0; i<lSrcHeight; ++i)
		{
			for (j=0; j<lSrcWidth; j++)
			{
				*(lpData+(lDestRowBytes*j+(lSrcHeight-i-1)*3))
					= *(lpOldBits+(lSrcRowBytes*i+j*3));
				*(lpData+(lDestRowBytes*j+(lSrcHeight-i-1)*3)+1)
					= *(lpOldBits+(lSrcRowBytes*i+j*3)+1);
				*(lpData+(lDestRowBytes*j+(lSrcHeight-i-1)*3)+2)
					= *(lpOldBits+(lSrcRowBytes*i+j*3)+2);
			}
		}
		break;
	}

	// cleanup
	GlobalUnlock(hDib);
	GlobalUnlock(hNewDib);
	WaitCursorEnd();
	
	return hNewDib;
}

/************************************************************************* 
 * 
 * FlipHorzDIB() 
 * 
 * Parameters: 
 * 
 * HDIB hDIB		 - handle of DIB to flip horzonly
 * 
 * Return Value: 
 * 
 * HDIB             - Handle to new DIB 
 * 
 * Description: 
 * 
 * This function flip DIB horzonly, and return
 * the flipped DIB in a new DIB handle, let the source DIB unchanged
 * 
 ************************************************************************/ 
HDIB FlipHorzDIB(HDIB hDib)
{
	WaitCursorBegin();

	// create new DIB
	DWORD dwDataLength = GlobalSize(hDib);
	HDIB hNewDib = GlobalAlloc(GHND, dwDataLength);
	if (! hNewDib)
	{
		WaitCursorEnd();
		return NULL;
	}
	LPBYTE lpDIB = (LPBYTE)GlobalLock(hNewDib);
	if (lpDIB == NULL)
	{
		WaitCursorEnd();
		return FALSE;
	}
	// old DIB buffer
	LPBYTE lpDIBSrc = (LPBYTE)GlobalLock(hDib);

	// copy LPBITMAPINFO from old to new
	memcpy(lpDIB, lpDIBSrc, sizeof(BITMAPINFOHEADER)+PaletteSize(lpDIBSrc));
	
	DWORD lSrcWidth = DIBWidth(lpDIBSrc);
	DWORD lSrcHeight = DIBHeight(lpDIBSrc);
	WORD wBitCount = ((LPBITMAPINFOHEADER)lpDIBSrc)->biBitCount;

	// get bytesbytes/pixel, bytes/row  
	double fColorBytes = (double)((double)wBitCount/8.0);
	DWORD lSrcRowBytes = WIDTHBYTES(lSrcWidth*((DWORD)wBitCount));

	// bits position
    LPBYTE lpOldBits = FindDIBBits(lpDIBSrc);
	LPBYTE lpData = FindDIBBits(lpDIB);

	// trandform bits
	DWORD i, j;
	switch (wBitCount)
	{
	case 1:
		for (i=0; i<lSrcHeight; ++i)
			for (j=0; j<lSrcWidth; ++j)
			{
				*(lpData+(lSrcRowBytes*i+(lSrcWidth-j-1)/8)) &= ~(1<<(7-((lSrcWidth-j-1)%8)));
				*(lpData+(lSrcRowBytes*i+(lSrcWidth-j-1)/8)) |= 
					((*(lpOldBits+(lSrcRowBytes*i+j/8))<<(j%8))>>7)<<(7-(lSrcWidth-j-1)%8);
			}
		break;
	case 4:
		for (i=0; i<lSrcHeight; ++i)
		{
			for (j=0; j<lSrcWidth; ++j)
			{
				*(lpData+(lSrcRowBytes*i+(lSrcWidth-j-1)/2)) &= ((lSrcWidth-j-1)%2) ? 0xf0 : 0x0f;
				*(lpData+(lSrcRowBytes*i+(lSrcWidth-j-1)/2)) |= 
					((*(lpOldBits+(lSrcRowBytes*i+j/2))<<(j%2 ? 4 : 0))>>4)<<(((lSrcWidth-j-1)%2) ? 0 : 4);
			}
		}
		break;
	case 8:
		for (i=0; i<lSrcHeight; ++i)
			for (j=0; j<lSrcWidth; ++j)
				*(lpData+(lSrcRowBytes*i+lSrcWidth-j-1))
					= *(lpOldBits+(lSrcRowBytes*i+j));
		break;
	case 24:
		for (i=0; i<lSrcHeight; ++i)
			for (j=0; j<lSrcWidth; j++)
			{
				*(lpData+(lSrcRowBytes*i+(lSrcWidth-j-1)*3))
					= *(lpOldBits+(lSrcRowBytes*i+j*3));
				*(lpData+(lSrcRowBytes*i+(lSrcWidth-j-1)*3)+1)
					= *(lpOldBits+(lSrcRowBytes*i+j*3)+1);
				*(lpData+(lSrcRowBytes*i+(lSrcWidth-j-1)*3)+2)
					= *(lpOldBits+(lSrcRowBytes*i+j*3)+2);
			}
		break;
	}

	GlobalUnlock(hDib);
	GlobalUnlock(hNewDib);
	WaitCursorEnd();

	return hNewDib;
}

/************************************************************************* 
 * 
 * FlipVertDIB() 
 * 
 * Parameters: 
 * 
 * HDIB hDIB		 - handle of DIB to flip vertically
 * 
 * Return Value: 
 * 
 * HDIB             - Handle to new DIB 
 * 
 * Description: 
 * 
 * This function flip DIB vertically, and return
 * the flipped DIB in a new DIB handle, let the source DIB unchanged
 * 
 ************************************************************************/ 
HDIB FlipVertDIB(HDIB hDib)
{
	WaitCursorBegin();

	// create new DIB
	DWORD dwDataLength = GlobalSize(hDib);
	HDIB hNewDib = GlobalAlloc(GHND, dwDataLength);
	if (! hNewDib)
	{
		WaitCursorEnd();
		return NULL;
	}
	LPBYTE lpDIB = (LPBYTE)GlobalLock(hNewDib);
	if (lpDIB == NULL)
	{
		WaitCursorEnd();
		return FALSE;
	}
	// old DIB buffer
	LPBYTE lpDIBSrc = (LPBYTE)GlobalLock(hDib);

	// copy LPBITMAPINFO from old to new
	memcpy(lpDIB, lpDIBSrc, sizeof(BITMAPINFOHEADER)+PaletteSize(lpDIBSrc));
	
	DWORD lSrcWidth = DIBWidth(lpDIBSrc);
	DWORD lSrcHeight = DIBHeight(lpDIBSrc);
	WORD wBitCount = ((LPBITMAPINFOHEADER)lpDIBSrc)->biBitCount;

	// get bytesbytes/pixel, bytes/row  
	double fColorBytes = (double)((double)wBitCount/8.0);
	DWORD lSrcRowBytes = WIDTHBYTES(lSrcWidth*((DWORD)wBitCount));

	// bits position
    LPBYTE lpOldBits = FindDIBBits(lpDIBSrc);
	LPBYTE lpData = FindDIBBits(lpDIB);

	// trandform bits
	for (DWORD i=0; i<lSrcHeight; ++i)
		for (DWORD j=0; j<lSrcRowBytes; ++j)
			*(lpData+(lSrcRowBytes*(lSrcHeight-i-1)+j))
				= *(lpOldBits+(lSrcRowBytes*i+j));

	GlobalUnlock(hDib);
	GlobalUnlock(hNewDib);
	WaitCursorEnd();

	return hNewDib;
}

/************************************************************************* 
 * 
 * ChangeDIBSize() 
 * 
 * Parameters: 
 * 
 * HDIB hDIB				 - handle of DIB to zoom
 * int nWidth, int nHeight   - new size of DIB
 * 
 * Return Value: 
 * 
 * HDIB             - Handle to new DIB 
 * 
 * Description: 
 * 
 * This function zoom a DIB to specified size, and return
 * the zoomed DIB in a new DIB handle, let the source DIB unchanged
 * 
 ************************************************************************/ 
HDIB ChangeDIBSize(HDIB hDIB, int nWidth, int nHeight)
{
    LPBITMAPINFO lpbmi = NULL; 
    LPBYTE       lpSourceBits, lpTargetBits, lpResult; 
    HDC			 hDC = NULL, hSourceDC, hTargetDC; 
    HBITMAP      hSourceBitmap, hTargetBitmap, hOldTargetBitmap, hOldSourceBitmap; 
    DWORD        dwSourceBitsSize, dwTargetBitsSize, dwTargetHeaderSize; 
	HDIB		 hNewDIB;
	DWORD		 dwSize;

	WaitCursorBegin();

	// Get DIB pointer
	if (! hDIB)
	{
		WaitCursorEnd();
		return NULL;
	}
	LPBITMAPINFO lpSrcDIB = (LPBITMAPINFO)GlobalLock(hDIB);
	if (! lpSrcDIB)
	{
		WaitCursorEnd();
		return NULL;
	}

    // Allocate and fill out a BITMAPINFO struct for the new DIB 
    dwTargetHeaderSize = sizeof( BITMAPINFOHEADER ) + PaletteSize(lpSrcDIB); 
    lpbmi = (LPBITMAPINFO)malloc( dwTargetHeaderSize ); 
	memcpy(lpbmi, lpSrcDIB, dwTargetHeaderSize);
    lpbmi->bmiHeader.biWidth = nWidth; 
    lpbmi->bmiHeader.biHeight = nHeight; 
 
    // Gonna use DIBSections and BitBlt() to do the conversion, so make 'em 
	hDC = GetDC( NULL ); 
    hTargetBitmap = CreateDIBSection( hDC, lpbmi, DIB_RGB_COLORS, (VOID **)&lpTargetBits, NULL, 0 ); 
    hSourceBitmap = CreateDIBSection( hDC, lpSrcDIB, DIB_RGB_COLORS, (VOID **)&lpSourceBits, NULL, 0 ); 
    hSourceDC = CreateCompatibleDC( hDC ); 
    hTargetDC = CreateCompatibleDC( hDC ); 
 
    // Flip the bits on the source DIBSection to match the source DIB 
    dwSourceBitsSize = lpSrcDIB->bmiHeader.biHeight * BytesPerLine((LPBYTE)&(lpSrcDIB->bmiHeader)); 
    dwTargetBitsSize = lpbmi->bmiHeader.biHeight * BytesPerLine((LPBYTE)&(lpbmi->bmiHeader)); 
    memcpy( lpSourceBits, FindDIBBits((LPBYTE)lpSrcDIB), dwSourceBitsSize ); 
    lpbmi->bmiHeader.biSizeImage = dwTargetBitsSize; 
 
    // Select DIBSections into DCs 
    hOldSourceBitmap = (HBITMAP)SelectObject( hSourceDC, hSourceBitmap ); 
    hOldTargetBitmap = (HBITMAP)SelectObject( hTargetDC, hTargetBitmap ); 
 
	// put old bitmap in new bitmap 
    SetStretchBltMode( hTargetDC, COLORONCOLOR ); 
    StretchBlt( hTargetDC, 0, 0, lpbmi->bmiHeader.biWidth, lpbmi->bmiHeader.biHeight, hSourceDC, 0, 0, lpSrcDIB->bmiHeader.biWidth, lpSrcDIB->bmiHeader.biHeight, SRCCOPY ); 
 
    // Clean up and delete the DCs 
    SelectObject( hSourceDC, hOldSourceBitmap ); 
    SelectObject( hTargetDC, hOldTargetBitmap ); 
    DeleteDC( hSourceDC ); 
    DeleteDC( hTargetDC ); 
    ReleaseDC( NULL, hDC ); 
 
    // Flush the GDI batch, so we can play with the bits 
    GdiFlush(); 
 
    // Allocate enough memory for the new CF_DIB, and copy bits 
	dwSize = dwTargetHeaderSize + dwTargetBitsSize;
	hNewDIB = GlobalAlloc(GHND, dwSize);
    lpResult = (LPBYTE)GlobalLock(hNewDIB);//malloc( dwTargetHeaderSize + dwTargetBitsSize ); 
    memcpy( lpResult, lpbmi, dwTargetHeaderSize ); 
    memcpy( FindDIBBits( (LPBYTE)lpResult ), lpTargetBits, dwTargetBitsSize ); 
 
    // final cleanup 
    DeleteObject( hTargetBitmap ); 
    DeleteObject( hSourceBitmap ); 
    free( lpbmi ); 
	GlobalUnlock(hDIB);
	GlobalUnlock(hNewDIB);
	WaitCursorEnd();
 
    return hNewDIB;
}

/************************************************************************* 
 * 
 * ChangeDIBCanvasSize() 
 * 
 * Parameters: 
 * 
 * HDIB hDIB				 - handle of DIB to change
 * int nWidth, int nHeight   - new size of DIB
 * int nPosition			 - position of source DIB in new DIB
 * 
 * Return Value: 
 * 
 * HDIB             - Handle to new DIB 
 * 
 * Description: 
 * 
 * This function change the canvas of DIB, and put source DIB in the
 * specified position of new DIB (canvas), and return the changed DIB 
 * in a new DIB handle, let the source DIB unchanged
 * 
 ************************************************************************/ 
HDIB ChangeDIBCanvasSize(HDIB hDIB, int nWidth, int nHeight, int nPosition)
{
    LPBITMAPINFO lpbmi = NULL; 
    LPBYTE       lpSourceBits, lpTargetBits, lpResult; 
    HDC			 hDC = NULL, hSourceDC, hTargetDC; 
    HBITMAP      hSourceBitmap, hTargetBitmap, hOldTargetBitmap, hOldSourceBitmap; 
    DWORD        dwSourceBitsSize, dwTargetBitsSize, dwTargetHeaderSize; 
	HDIB		 hNewDIB;
	DWORD		 dwSize;

	WaitCursorBegin();

	// Get DIB pointer
	if (! hDIB)
	{
		WaitCursorEnd();
		return NULL;
	}
	LPBITMAPINFO lpSrcDIB = (LPBITMAPINFO)GlobalLock(hDIB);
	if (! lpSrcDIB)
	{
		WaitCursorEnd();
		return NULL;
	}

    // Allocate and fill out a BITMAPINFO struct for the new DIB 
    dwTargetHeaderSize = sizeof( BITMAPINFOHEADER ) + PaletteSize(lpSrcDIB); 
    lpbmi = (LPBITMAPINFO)malloc( dwTargetHeaderSize ); 
	memcpy(lpbmi, lpSrcDIB, dwTargetHeaderSize);
    lpbmi->bmiHeader.biWidth = nWidth; 
    lpbmi->bmiHeader.biHeight = nHeight; 
 
    // Gonna use DIBSections and BitBlt() to do the conversion, so make 'em 
	hDC = GetDC( NULL ); 
    hTargetBitmap = CreateDIBSection( hDC, lpbmi, DIB_RGB_COLORS, (VOID **)&lpTargetBits, NULL, 0 ); 
    hSourceBitmap = CreateDIBSection( hDC, lpSrcDIB, DIB_RGB_COLORS, (VOID **)&lpSourceBits, NULL, 0 ); 
    hSourceDC = CreateCompatibleDC( hDC ); 
    hTargetDC = CreateCompatibleDC( hDC ); 
 
    // Flip the bits on the source DIBSection to match the source DIB 
    dwSourceBitsSize = lpSrcDIB->bmiHeader.biHeight * BytesPerLine((LPBYTE)&(lpSrcDIB->bmiHeader)); 
    dwTargetBitsSize = lpbmi->bmiHeader.biHeight * BytesPerLine((LPBYTE)&(lpbmi->bmiHeader)); 
    memcpy( lpSourceBits, FindDIBBits((LPBYTE)lpSrcDIB), dwSourceBitsSize ); 
    lpbmi->bmiHeader.biSizeImage = dwTargetBitsSize; 
 
    // Select DIBSections into DCs 
    hOldSourceBitmap = (HBITMAP)SelectObject( hSourceDC, hSourceBitmap ); 
    hOldTargetBitmap = (HBITMAP)SelectObject( hTargetDC, hTargetBitmap ); 
 
	// old bitmap position in new bitmap
	int x = 0;
	int y = 0;
	int nOldWidth = lpSrcDIB->bmiHeader.biWidth;
	int nOldHeight = lpSrcDIB->bmiHeader.biHeight; 
 	switch (nPosition)
	{
	case LEFT_UP:
		x = 0;
		y = 0;
		break;
	case CENTER_UP:
		x = (nWidth-nOldWidth)/2;
		y = 0;
		break;
	case RIGHT_UP:
		x = nWidth-nOldWidth;
		y = 0;
		break;
	case LEFT_CENTER:
		x = 0;
		y = (nHeight-nOldHeight)/2;
		break;
	case CENTER_CENTER:
		x = (nWidth-nOldWidth)/2;
		y = (nHeight-nOldHeight)/2;
		break;
	case CENTER_RIGHT:
		x = nWidth-nOldWidth;
		y = (nHeight-nOldHeight)/2;
		break;
	case LEFT_DOWN:
		x = 0;
		y = nHeight-nOldHeight;
		break;
	case CENTER_DOWN:
		x = (nWidth-nOldWidth)/2;
		y = nHeight-nOldHeight;
		break;
	case RIGHT_DOWN:
		x = nWidth-nOldWidth;
		y = nHeight-nOldHeight;
		break;
	}

	// first clear the new bitmap with whitwness
	HBRUSH hBrush = CreateSolidBrush(RGB(255,255,255));
	HPEN hPen = CreatePen(PS_SOLID, 1, RGB(255,255,255));
	HBRUSH hOldBrush = (HBRUSH)SelectObject(hTargetDC, hBrush);
	HPEN hOldPen = (HPEN)SelectObject(hTargetDC, hPen);
	Rectangle(hTargetDC, 0, 0, nWidth, nHeight);
	SelectObject(hTargetDC, hOldBrush);
	SelectObject(hTargetDC, hOldPen);
	// put old bitmap in new bitmap 
    BitBlt(hTargetDC, x, y, nOldWidth, nOldHeight, hSourceDC, 0, 0, SRCCOPY); 
 
    // Clean up and delete the DCs 
    SelectObject( hSourceDC, hOldSourceBitmap ); 
    SelectObject( hTargetDC, hOldTargetBitmap ); 
    DeleteDC( hSourceDC ); 
    DeleteDC( hTargetDC ); 
    ReleaseDC( NULL, hDC ); 
 
    // Flush the GDI batch, so we can play with the bits 
    GdiFlush(); 
 
    // Allocate enough memory for the new CF_DIB, and copy bits 
	dwSize = dwTargetHeaderSize + dwTargetBitsSize;
	hNewDIB = GlobalAlloc(GHND, dwSize);
    lpResult = (LPBYTE)GlobalLock(hNewDIB);//malloc( dwTargetHeaderSize + dwTargetBitsSize ); 
    memcpy( lpResult, lpbmi, dwTargetHeaderSize ); 
    memcpy( FindDIBBits( (LPBYTE)lpResult ), lpTargetBits, dwTargetBitsSize ); 
 
    // final cleanup 
    DeleteObject( hTargetBitmap ); 
    DeleteObject( hSourceBitmap ); 
    free( lpbmi ); 
	GlobalUnlock(hDIB);
	GlobalUnlock(hNewDIB);
	WaitCursorEnd();
 
    return hNewDIB;
}



//////////////////////////////////////////////////////////////////////////
}	//
